package com.zhangln.mountbuzhou.core.service;

import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.zhangln.mountbuzhou.common.SpringUtils;
import com.zhangln.mountbuzhou.core.RegContent;
import com.zhangln.mountbuzhou.dao.TbConnectionLogDao;
import com.zhangln.mountbuzhou.dao.TbOnlineDao;
import com.zhangln.mountbuzhou.dao.TbPushLogDao;
import com.zhangln.mountbuzhou.dto.BaseResponse;
import com.zhangln.mountbuzhou.dto.request.PushMsgReq;
import com.zhangln.mountbuzhou.entity.TbAuthEntity;
import com.zhangln.mountbuzhou.entity.TbPushLogEntity;
import com.zhangln.mountbuzhou.service.AuthService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.net.Inet4Address;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

/**
 * @author sherry
 * @description
 * @date Create in 2020/4/27
 * @modified By:
 */
@Slf4j
@Service
public class WsService {

    @Value("${server.port:8080}")
    private Integer port;

    @Value("${buzhou.auth.timeout:10}")
    private Integer timeout;

    private final TbConnectionLogDao tbConnectionLogDao;
    private final TbPushLogDao tbPushLogDao;
    private final TbOnlineDao tbOnlineDao;

    private final RedisTemplate redisTemplate;

    public WsService(TbConnectionLogDao tbConnectionLogDao, TbPushLogDao tbPushLogDao, TbOnlineDao tbOnlineDao, RedisTemplate redisTemplate) {
        this.tbConnectionLogDao = tbConnectionLogDao;
        this.tbPushLogDao = tbPushLogDao;
        this.tbOnlineDao = tbOnlineDao;
        this.redisTemplate = redisTemplate;
    }

    /**
     * 设备连接
     *
     * @param channelId
     */
    public void connected(String channelId) {
        tbConnectionLogDao.saveConnectionLog(channelId);
    }

    /**
     * 心跳
     *
     * @param channelId
     */
    public void keepAlive(String channelId) {
        tbConnectionLogDao.savePingLog(channelId);
//        更新在线设备的更新时间
        tbOnlineDao.updateLastUpdatetime(channelId);
    }

    /**
     * 检查token合法性
     *
     * @param token
     * @return
     */
    public BaseResponse checkToken(String token) {
        AuthService authService = SpringUtils.getBean(AuthService.class);
        BaseResponse baseResponse = authService.checkToken(token);
        log.info("token :{} 校验结果：{}", token, baseResponse);
        return baseResponse;
    }

    /**
     * @param channelId
     * @param msg
     */
    public void savePushLog(String channelId, String msg) {
        PushMsgReq pushMsgReq = new PushMsgReq();
        pushMsgReq.setPushId(UUID.randomUUID().toString());
        pushMsgReq.setMsgId(UUID.randomUUID().toString());
        pushMsgReq.setBody(msg);
        tbPushLogDao.savePushLog(UUID.randomUUID().toString(), channelId, pushMsgReq);
    }

    /**
     * 注册成功
     *
     * @param channelId
     * @param jsonObjStr
     * @param resp
     */
    public void regSuccess(String channelId, String jsonObjStr, String resp) {
        RegContent regContent = JSONObject.parseObject(jsonObjStr, RegContent.class);
//        注册成功日志
        tbConnectionLogDao.saveConnectionSuccLog(channelId, regContent);
//        客户在线登记
        tbOnlineDao.regSuccess(channelId, regContent.getArea(), regContent.getOrgCode()
                , regContent.getDevice(), regContent.getUserId(), getInstanceFlag());
//        推送日志
        PushMsgReq pushMsgReq = new PushMsgReq();
        pushMsgReq.setPushId(UUID.randomUUID().toString());
        pushMsgReq.setMsgId(UUID.randomUUID().toString());
        pushMsgReq.setBody(resp);
        tbPushLogDao.savePushLog(UUID.randomUUID().toString(), channelId, pushMsgReq);
    }

    /**
     * 计算实例标识
     * 这个规则可以变化
     * 这个计算规则的变化，往往和部署方式有关
     * <p>
     * 第一版计算规则：运行环境的IP:PORT
     *
     * @return
     */
    public String getInstanceFlag() {
        InetAddress ip4 = null;
        try {
            ip4 = Inet4Address.getLocalHost();
        } catch (UnknownHostException e) {
            log.error(e.getMessage(), e);
        }
//        这个地址是K8S为Pod生成的内网ip，保证整个集群中都是不重复的
        String hostAddress = ip4.getHostAddress();
        return hostAddress + ":" + port;
    }

    /**
     * 客户端连接被拒绝
     *
     * @param channelId
     */
    public void connectionRefused(String channelId) {
        tbConnectionLogDao.saveConnectionRefusedLog(channelId);
    }

    public void handlerRemoved(String channelId, String msg) {
//        连接状态日志变更
        tbConnectionLogDao.saveHandlerRemovedLog(channelId, msg);
//        删除设备在线记录
        tbOnlineDao.delete(channelId);
    }

    /**
     * 重置token的过期时间
     *
     * @param token
     */
    public void resetTokenTimeout(String token) {
        try {
            TbAuthEntity tbAuthEntity = (TbAuthEntity) redisTemplate.opsForValue().get("TOKEN:" + token);
            redisTemplate.opsForValue().set("TOKEN:" + token, tbAuthEntity, timeout, TimeUnit.MINUTES);
        } catch (Exception e) {
            log.error("心跳 重置 token异常 " + e.getMessage(), e);
        }

    }

    /**
     * 消息确认送达
     *
     * @param channelId
     * @param msgId
     */
    public void msgConfirm(String channelId, String msgId) {
        log.info("WebSocket客户端消息确认：\nchannelId:{}\nmsgId:{}", channelId, msgId);
        List<TbPushLogEntity> list = tbPushLogDao.list(new QueryWrapper<TbPushLogEntity>()
                .eq(TbPushLogEntity.CHANNEL_ID, channelId)
                .eq(TbPushLogEntity.MSG_ID, msgId));
        if (!CollectionUtils.isEmpty(list)) {
            list.stream()
                    .forEach(tbPushLogEntity -> {
                        tbPushLogDao.msgConfirm(channelId, msgId);
                    });
        }
    }
}
